Autores: Cindy Naranjo y Jairo Iván Ordóñez
Facultad: Ingeniería
Programa: Especialización Analítica Estratégica de Datos
2020-2
*Corte al Q4 de 2019 según MINTIC https://colombiatic.mintic.gov.co/679/w3-propertyvalue-47274.html
import mysql.connector
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
import pandas_profiling
from plotly.subplots import make_subplots
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn import metrics
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
from sklearn.tree import export_graphviz
import graphviz
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from time import time
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import plot_confusion_matrix
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
pd.options.mode.chained_assignment = None
In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The text.latex.preview rcparam was deprecated in Matplotlib 3.3 and will be removed two minor releases later. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The mathtext.fallback_to_cm rcparam was deprecated in Matplotlib 3.3 and will be removed two minor releases later. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: Support for setting the 'mathtext.fallback_to_cm' rcParam is deprecated since 3.3 and will be removed two minor releases later; use 'mathtext.fallback : 'cm' instead. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The validate_bool_maybe_none function was deprecated in Matplotlib 3.3 and will be removed two minor releases later. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The savefig.jpeg_quality rcparam was deprecated in Matplotlib 3.3 and will be removed two minor releases later. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The keymap.all_axes rcparam was deprecated in Matplotlib 3.3 and will be removed two minor releases later. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The animation.avconv_path rcparam was deprecated in Matplotlib 3.3 and will be removed two minor releases later. In C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\matplotlib\mpl-data\stylelib\_classic_test.mplstyle: The animation.avconv_args rcparam was deprecated in Matplotlib 3.3 and will be removed two minor releases later.
dfConfig = pd.read_csv("../data/config.txt", sep="|")
conexion = mysql.connector.connect(host=dfConfig['host'][0],
port=dfConfig['port'][0],
user=dfConfig['user'][0],
password=dfConfig['pass'][0],
db=dfConfig['schema'][0])
with open("../data/queriePaquetes.sql") as querie:
datoConsulta = querie.read()
dfSQL = pd.read_sql(datoConsulta, conexion)
conexion.close()
Ejmplo de los datos extraídos:
dfSQL.head()
| offerId | date | nombre_dia | tipo_dia | Ingreso | Cantidad_Vendidos | consumo_INTERNET | Consumo_Whatsapp | Consumo_Facebook | Consumo_Twitter | Consumo_Instagram | Consumo_Snapchat | Consumo_Youtube | Consumo_ClaroVideo | Consumo_Waze | Consumo_GoogleMaps | Consumo_EasyTaxi | Consumo_AppTaxi | Consumo_WAP | tipoPaquete | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30091 | 2020-06-16 | martes | HABIL | 0.0 | 0.0 | 0.000000e+00 | 2.437687e+12 | 0.000000e+00 | 0.000000e+00 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | Datos |
| 1 | 30093 | 2020-06-16 | martes | HABIL | 0.0 | 0.0 | 0.000000e+00 | 2.763205e+11 | 2.343967e+11 | 9.434252e+09 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | Datos |
| 2 | 30094 | 2020-06-16 | martes | HABIL | 0.0 | 0.0 | 0.000000e+00 | 1.831004e+11 | 1.275699e+11 | 4.546316e+09 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | Datos |
| 3 | 30125 | 2020-06-16 | martes | HABIL | 0.0 | 0.0 | 2.414058e+09 | 0.000000e+00 | 0.000000e+00 | 0.000000e+00 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | Hajj |
| 4 | 30126 | 2020-06-16 | martes | HABIL | 0.0 | 0.0 | 0.000000e+00 | 4.082384e+09 | 3.682436e+09 | 1.430274e+07 | 81746057.0 | 31413995.0 | 251789951.0 | 0.0 | 19769.0 | 0.0 | 166202225.0 | 133173.0 | 0.0 | Hajj |
dfDescDataset = pd.read_csv("../data/descripcionDataset.txt", sep="|")
pd.set_option('display.max_colwidth', 2000)
dfDescDataset
| Campo | Tipo de Campo | Descripción | |
|---|---|---|---|
| 0 | OfferId | INTEGER | Identificador del Paquete del dataset. |
| 1 | date | DATE | Fecha del registro en formato YYYY-MM-DD. |
| 2 | nombre_dia | VARCHAR | Identifica el Nombre del día de la fecha (Domingo, Lunes, Martes, Miércoles, Jueves, Viernes, Sábado). |
| 3 | tipo_dia | VARCHAR | Clasificación del día (HABIL, FIN DE SEMANA, FESTIVO). |
| 4 | Ingreso | FLOAT | Total de ingreso del paquete para el día en cuestión. |
| 5 | Cantidad_Vendidos | INTEGER | Total de unidades vendidas del paquete par el día en cuestión. |
| 6 | consumo_INTERNET | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio INTERNET. |
| 7 | Consumo_Whatsapp | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Whatsapp. |
| 8 | Consumo_Facebook | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Facebook. |
| 9 | Consumo_Twitter | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Twitter. |
| 10 | Consumo_Instagram | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Instagram. |
| 11 | Consumo_Snapchat | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Snapchat. |
| 12 | Consumo_Youtube | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Youtube. |
| 13 | Consumo_ClaroVideo | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Claro Video. |
| 14 | Consumo_Waze | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Waze. |
| 15 | Consumo_GoogleMaps | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Google Maps. |
| 16 | Consumo_EasyTaxi | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio Easy Taxi. |
| 17 | Consumo_AppTaxi | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio App Taxi. |
| 18 | Consumo_WAP | FLOAT | Total del consumo de navegación en Bytes utilizando el servicio WAP. |
| 19 | tipoPaquete | VARCHAR | Variable dependiente que identifica el tipo de paquete. |
profile = pandas_profiling.ProfileReport(dfSQL)
profile.to_file("../results/pofiling.html")
dfSQL['consumo_total']= dfSQL['consumo_INTERNET'] + dfSQL['Consumo_Whatsapp'] + dfSQL['Consumo_Facebook'] + dfSQL['Consumo_Twitter'] + dfSQL['Consumo_Instagram'] + dfSQL['Consumo_Snapchat'] + dfSQL['Consumo_Youtube'] + dfSQL['Consumo_ClaroVideo'] + dfSQL['Consumo_Waze'] + dfSQL['Consumo_GoogleMaps'] + dfSQL['Consumo_EasyTaxi'] + dfSQL['Consumo_AppTaxi'] + dfSQL['Consumo_WAP']
dfSQL['consumo_total']= dfSQL['consumo_total']/1024/1024
dfdatos=dfSQL[dfSQL['tipoPaquete']=='Datos']
dfdatos = dfdatos.groupby(['date']).sum().reset_index()
dftodoinc=dfSQL[dfSQL['tipoPaquete']=='Todo Incluido']
dftodoinc = dftodoinc.groupby(['date']).sum().reset_index()
Quisimos ver cómo es el consumo total de navegación por los paquetes que nos interesan por día:
fig2 = go.Figure()
fig2.add_trace(go.Scatter(
x=dfdatos['date']
,y=dfdatos['consumo_total']
,marker=dict(
color="blue"
)
,name="Paquetes de Datos"
#,showlegend=False
))
fig2.add_trace(go.Scatter(
x=dftodoinc['date']
,y=dftodoinc['consumo_total']
,marker=dict(
color="red"
)
,name="Paquetes Todo Incluido"
#,showlegend=False
))
fig2.update_layout(title="Tendencia de Consumos por Tipo de Paquete")
fig2.show()
dfBoxPlot = dfSQL.groupby(['date','tipoPaquete','tipo_dia','nombre_dia']).sum().reset_index()
dfBoxPlot = dfBoxPlot[dfBoxPlot['tipoPaquete'].isin(['Datos','Todo Incluido'])]
fig = px.box(dfBoxPlot,
y="consumo_total",
color="tipoPaquete",
title="BoxPlot Consumo Total por día por tipo de paquete")
fig.show()
Haciendo una comparación contra el anterior gráfico, podemos evidenciar que, son más los paquetes de Datos que se venden que los Todo Incluido, sin embargo, aquellos Paquetes Todo Incluido mueven más volumen de Navegación.
fig = px.box(dfBoxPlot,
y="Cantidad_Vendidos",
color="tipoPaquete",
title="BoxPlot Cantidad de Paquetes Vendidos por día por tipo")
fig.show()
Con este gráfico podemos evidenciar las relaciones de los consumos por los servicios de redes sociales y ver que, gráficamente se puede clasificar los tipos de Paquete.
Es evidente la diferencia entre los paquetes de Datos y los Todo Incluido incluso a nivel de Servicio
fig = px.scatter_matrix(dfBoxPlot,
dimensions=['Consumo_Whatsapp','Consumo_Facebook','Consumo_Twitter','Consumo_Instagram','Consumo_Snapchat'],
color='tipoPaquete',
title='Matriz de Relación de Consumos por día por Servicios de Redes Sociales',
labels = {col:col.replace('Consumo_','') for col in dfBoxPlot.columns})
fig.update_traces(diagonal_visible=False)
fig.show()
Ahora, para evaluar un poco más al detalle cada uno de los servicios, hemos creado un histograma por cada servicio, discriminado por el tipo de Paquete
dfTodoIncluido = dfBoxPlot[dfBoxPlot['tipoPaquete'].isin(['Todo Incluido'])]
dfPaqDatos = dfBoxPlot[dfBoxPlot['tipoPaquete'].isin(['Datos'])]
fig = make_subplots(rows=1
, cols=2
,subplot_titles=("Histograma Whatsapp Paquetes Todo Incluido"
,"Histograma Whatsapp Paquetes Datos"))
fig.add_trace(go.Histogram(x=dfTodoIncluido['Consumo_Whatsapp'],
nbinsx=30,
opacity=0.8),
row=1, col=1)
fig.add_trace(go.Histogram(x=dfPaqDatos['Consumo_Whatsapp'],
nbinsx=30,
opacity=0.8),
row=1, col=2)
fig.update_layout(showlegend=False)
fig = make_subplots(rows=1
, cols=2
,subplot_titles=("Histograma Facebook Paquetes Todo Incluido"
,"Histograma Facebook Paquetes Datos"))
fig.add_trace(go.Histogram(x=dfTodoIncluido['Consumo_Facebook'],
nbinsx=30,
opacity=0.8),
row=1, col=1)
fig.add_trace(go.Histogram(x=dfPaqDatos['Consumo_Facebook'],
nbinsx=30,
opacity=0.8),
row=1, col=2)
fig.update_layout(showlegend=False)
fig = make_subplots(rows=1
, cols=2
,subplot_titles=("Histograma Twitter Paquetes Todo Incluido"
,"Histograma Twitter Paquetes Datos"))
fig.add_trace(go.Histogram(x=dfTodoIncluido['Consumo_Twitter'],
nbinsx=30,
opacity=0.8),
row=1, col=1)
fig.add_trace(go.Histogram(x=dfPaqDatos['Consumo_Twitter'],
nbinsx=30,
opacity=0.8),
row=1, col=2)
fig.update_layout(showlegend=False)
fig = make_subplots(rows=1
, cols=2
,subplot_titles=("Histograma Internet Paquetes Todo Incluido"
,"Histograma Internet Paquetes Datos"))
fig.add_trace(go.Histogram(x=dfTodoIncluido['consumo_INTERNET'],
nbinsx=30,
opacity=0.8),
row=1, col=1)
fig.add_trace(go.Histogram(x=dfPaqDatos['consumo_INTERNET'],
nbinsx=30,
opacity=0.8),
row=1, col=2)
fig.update_layout(showlegend=False)
fig = make_subplots(rows=1
, cols=2
,subplot_titles=("Histograma Instagram Paquetes Todo Incluido"
,"Histograma Instagram Paquetes Datos"))
fig.add_trace(go.Histogram(x=dfTodoIncluido['Consumo_Instagram'],
nbinsx=30,
opacity=0.8),
row=1, col=1)
fig.add_trace(go.Histogram(x=dfPaqDatos['Consumo_Instagram'],
nbinsx=30,
opacity=0.8),
row=1, col=2)
fig.update_layout(showlegend=False)
fig = make_subplots(rows=1
, cols=2
,subplot_titles=("Histograma Snapchat Paquetes Todo Incluido"
,"Histograma Snapchat Paquetes Datos"))
fig.add_trace(go.Histogram(x=dfTodoIncluido['Consumo_Snapchat'],
nbinsx=30,
opacity=0.8),
row=1, col=1)
fig.add_trace(go.Histogram(x=dfPaqDatos['Consumo_Snapchat'],
nbinsx=30,
opacity=0.8),
row=1, col=2)
fig.update_layout(showlegend=False)
Antes de continuar con el entrenamiento y prueba de los modelos, vamos a crear normalizadores de datos para los tipos de dato categóricos y dividir nuestra muestra en entrenamiento y prueba
def days_normalizer(dayname):
if dayname == 'domingo':
return 1
if dayname == 'lunes':
return 2
if dayname == 'martes':
return 3
if dayname == 'miércoles':
return 4
if dayname == 'jueves':
return 5
if dayname == 'viernes':
return 6
if dayname == 'sábado':
return 7
def type_day_normalizer(typeDay):
if typeDay == 'HABIL':
return 1
if typeDay == 'FIN_DE_SEMANA':
return 2
if typeDay == 'FESTIVO':
return 3
dfModelo = dfSQL[dfSQL['tipoPaquete'].isin(['Datos','Todo Incluido'])]
dfModelo['nombre_dia'] = dfModelo['nombre_dia'].apply(lambda nombreDia: days_normalizer(nombreDia))
dfModelo['tipo_dia'] = dfModelo['tipo_dia'].apply(lambda tipoDia: type_day_normalizer(tipoDia))
Para todos los modelos vamos a utilizar las mismas particiones de entrenamiento y pruebas para ser justos al momento de elegir el mejor modelo. Elegimos una partición de 80% de datos para entrenar los modelos y 20% para probar.
varIndep = dfModelo[['nombre_dia' ,'tipo_dia' ,'Ingreso' ,'Cantidad_Vendidos' ,'consumo_INTERNET' ,'Consumo_Whatsapp' ,'Consumo_Facebook' ,'Consumo_Twitter' ,'Consumo_Instagram' ,'Consumo_Snapchat' ,'Consumo_Youtube' ,'Consumo_ClaroVideo' ,'Consumo_Waze' ,'Consumo_GoogleMaps' ,'Consumo_EasyTaxi' ,'Consumo_AppTaxi' ,'Consumo_WAP']].values
varDep = dfModelo[['tipoPaquete']].values
X_train, X_test, y_train, y_test = train_test_split(varIndep
, varDep
, test_size=0.20
, random_state=20201115
, stratify=varDep)
Validamos que la distribución de las categorías de nuestras particiones sea equitativa basado en la variable objetivo
plt.figure(figsize=(10,6))
plt.hist(y_train, label="Train")
plt.hist(y_test, label="Test")
plt.legend()
<matplotlib.legend.Legend at 0x2126b28c208>
El árbol de decisión es un modelo de clasificación Supervisado que, basado en las variables independientes intenta predecir la variable objetivo o dependiente utilizando funciones lógicas de comparación anidadas sobre las variables independientes. Para este modelo vamos a utilizar un listado de profundidades para identificar cuál es el parámetro que mejor predice de acuerdo a la partición de entrenamiento.
max_depth_values = list([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,30, 40, 50])
Entrenamos nuestros árboles de decisión de acuerdo a las profundidades definidas y los vamos probando con la partición de prueba:
train_error = []
generalization_error = []
for depth in max_depth_values:
decision_tree = DecisionTreeClassifier(max_depth=depth, class_weight='balanced')
decision_tree.fit(X_train, y_train)
train_error.append(1 - decision_tree.score(X_train, y_train))
generalization_error.append(1 - decision_tree.score(X_test, y_test))
Luego de los entrenamientos, Identificamos cuál es la profundidad con menor error en train y en test:
df_error_train = pd.DataFrame(train_error, columns= ['Error_train'], index=max_depth_values)
df_error_test = pd.DataFrame(generalization_error, columns= ['Error_test'], index=max_depth_values)
df_error = pd.concat([df_error_train,df_error_test],axis=1 )
df_error['error_dif'] = df_error['Error_test'] - df_error['Error_train']
df_error[df_error['error_dif'] == df_error['error_dif'].min()]
| Error_train | Error_test | error_dif | |
|---|---|---|---|
| 3 | 0.266497 | 0.263347 | -0.003149 |
De acuerdo a los valores generados, la profundidad con mejor balance en error de entrenamiento y prueba, es 3, sin embargo, vamos a ver cómo es el comportamiento de los errores de acuerdo a la profundidad, mediante un gráfico:
plt.figure(figsize = (10, 6))
plt.plot(max_depth_values, train_error, label="Entrenamiento")
plt.plot(max_depth_values, generalization_error, label="Prueba")
plt.xticks(max_depth_values)
plt.xlabel("Profundidad máxima")
plt.ylabel("Error")
plt.arrow(7, 0.06, 0, 0.12, head_length=0.02, fc='k', ec='k')
plt.text(3, 0.05, 'Punto de balance')
plt.legend();
El gráfico nos muestra que, el mejor balance de error entre los datos de entrenamiento y de prueba, está mejor posicionado sobre las 7 profundidades del árbol, por lo tanto, entrenamos nuestro modelo con este parámetro de profundidades.
arbolPaquetes = DecisionTreeClassifier(criterion="entropy", max_depth = 7)
arbolPaquetes.fit(X_train,y_train)
DecisionTreeClassifier(criterion='entropy', max_depth=7)
Probamos nuestro modelo con los datos de prueba y medimos la Fiabilidad basado en el verdadero resultado, y lo que el modelo predice:
yPredictDT = arbolPaquetes.predict(X_test)
print("Fiabilidad del ábol de decisión: ", round(metrics.accuracy_score(y_test, yPredictDT),4))
Fiabilidad del ábol de decisión: 0.8572
Ahora creamos la matriz de confusión para ver de manera gráfica cómo predijo el modelo y cuáles eran los valores reales.
%config InlineBackend.figure_format = 'svg'
fig = plt.figure(figsize=(15,15))
ax1 = fig.add_subplot(1,3,1)
plot_confusion_matrix(arbolPaquetes, X_test, y_test, cmap = 'Reds', ax=ax1).im_.colorbar.remove()
plt.title("Decision Tree")
Text(0.5, 1.0, 'Decision Tree')
La regresión logística es un modelo de tipo regresión utilizado para predecir el resultado de una variable categórica en función de las variables predictoras. Calcula los estimadores más representativos sobre las variables independientes para calcular el resultado de la variable dependiente.
Entrenamos nuestro modelo con los mismos datos que utilizamos para el árbol de decisión
logreg = LogisticRegression(class_weight='balanced')
logreg.fit(X_train, y_train.ravel())
C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\sklearn\linear_model\_logistic.py:764: ConvergenceWarning:
lbfgs failed to converge (status=2):
ABNORMAL_TERMINATION_IN_LNSRCH.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
LogisticRegression(class_weight='balanced')
Probamos nuestro modelo con los datos de prueba y medimos la Fiabilidad basado en el verdadero resultado, y lo que el modelo predice:
yPredictLR = logreg.predict(X_test)
print("Fiabilidad de la regresión logística es: ", round(metrics.accuracy_score(y_test, yPredictLR),4))
Fiabilidad de la regresión logística es: 0.6749
Ahora creamos la matriz de confusión para ver de manera gráfica cómo predijo el modelo y cuáles eran los valores reales.
%config InlineBackend.figure_format = 'svg'
fig = plt.figure(figsize=(15,15))
ax1 = fig.add_subplot(1,3,1)
plot_confusion_matrix(logreg, X_test, y_test, cmap = 'Reds', ax=ax1).im_.colorbar.remove()
plt.title("Logistic Regression")
Text(0.5, 1.0, 'Logistic Regression')
El algoritmo clasificador "Bayesiano Ingenuo", son algoritmos de clasificación de apredizaje auto´matico el cual se basan en una técnica de clasificación estadística llamada "teorema de Bayes". El algoritmo asume que todas las variables independientes también son independientes entre si.
Nuevamente, empleamos el proceso de entrenamiento
nb = MultinomialNB()
nb.fit(X_train, y_train.ravel())
MultinomialNB()
Probamos nuestro modelo con los datos de prueba y medimos la Fiabilidad basado en el verdadero resultado, y lo que el modelo predice:
yPredictNB = nb.predict(X_test)
print("Fiabilidad de Bayes es: ", round(metrics.accuracy_score(y_test, yPredictNB),4))
Fiabilidad de Bayes es: 0.6159
Ahora creamos la matriz de confusión para ver de manera gráfica cómo predijo el modelo y cuáles eran los valores reales.
%config InlineBackend.figure_format = 'svg'
fig = plt.figure(figsize=(15,15))
ax1 = fig.add_subplot(1,3,1)
plot_confusion_matrix(nb, X_test, y_test, cmap = 'Reds', ax=ax1).im_.colorbar.remove()
plt.title("Naive Bayes")
Text(0.5, 1.0, 'Naive Bayes')
Las máquinas de Soporte Vectorial (SVM por sus siglas en inglés Support Vector Machine), son conjuntos de algoritmos de aprendizaje supervisado que permiten la resolución de problemas de clasificación y regresión. Utiliza un hiperplano para poder separar las clases en espacios lo más amplios posibles definiendo un vector entre los 2 puntos o clases que queramos predecir.
Nuevamente, empleamos nuestro proceso de entrenamiento con los datos particionados:
svm = LinearSVC(class_weight='balanced')
svm.fit(X_train, y_train.ravel())
C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\sklearn\svm\_base.py:977: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
LinearSVC(class_weight='balanced')
Probamos nuestro modelo con los datos de prueba y medimos la Fiabilidad basado en el verdadero resultado, y lo que el modelo predice:
yPredictSVM = svm.predict(X_test)
print("Fiabilidad de la Máquina de Soporte Vectorial es: ", round(metrics.accuracy_score(y_test, yPredictSVM),4))
Fiabilidad de la Máquina de Soporte Vectorial es: 0.5515
Ahora creamos la matriz de confusión para ver de manera gráfica cómo predijo el modelo y cuáles eran los valores reales.
%config InlineBackend.figure_format = 'svg'
fig = plt.figure(figsize=(15,15))
ax1 = fig.add_subplot(1,3,1)
plot_confusion_matrix(svm, X_test, y_test, cmap = 'Reds', ax=ax1).im_.colorbar.remove()
plt.title("SVM")
Text(0.5, 1.0, 'SVM')
El rándom Forest es una combinación de árboles de decisión no correlacionados donde ejecuta una cantidad definida de árboles, promedia el resultado e identifica cuáles son los mejores parámetros y el mejor score para la clasificación.
Para este proyecto hemos definido una grilla de parámetros el cual, mediante validación cruzada se realizarán la cantidad de modelos necesarios para identificar los mejores parámetros a utilizar en nuestro random Forest. Este proceso puede tardar dada la cantidad de combinaciones que identifique la validación cruzada basado en los parámetros enviados
param_grid = {
'bootstrap': [True],
'max_depth': [80, 90, 100, 110],
'max_features': [2, 3, 6 ],
'min_samples_leaf': [3, 4, 5],
'min_samples_split': [8, 10, 12],
'n_estimators': [100, 200, 300, 1000]
}
rf = RandomForestClassifier()
grid_search = GridSearchCV(estimator = rf, param_grid = param_grid,
cv = 3, n_jobs = -1, verbose = 2)
grid_search.fit(X_train, y_train)
rfc_pred = grid_search.predict(X_test)
rfc_pred
Fitting 3 folds for each of 1008 candidates, totalling 3024 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers. [Parallel(n_jobs=-1)]: Done 25 tasks | elapsed: 42.4s [Parallel(n_jobs=-1)]: Done 146 tasks | elapsed: 5.3min [Parallel(n_jobs=-1)]: Done 349 tasks | elapsed: 17.5min [Parallel(n_jobs=-1)]: Done 632 tasks | elapsed: 37.3min [Parallel(n_jobs=-1)]: Done 997 tasks | elapsed: 58.7min [Parallel(n_jobs=-1)]: Done 1442 tasks | elapsed: 93.3min [Parallel(n_jobs=-1)]: Done 1969 tasks | elapsed: 120.8min [Parallel(n_jobs=-1)]: Done 2576 tasks | elapsed: 160.0min [Parallel(n_jobs=-1)]: Done 3024 out of 3024 | elapsed: 194.0min finished C:\Jairo.Ordonez\2. Personal\Data Science Program\Anaconda3\lib\site-packages\sklearn\model_selection\_search.py:765: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples,), for example using ravel().
array(['Todo Incluido', 'Datos', 'Datos', ..., 'Datos', 'Datos',
'Todo Incluido'], dtype=object)
Luego de realizar la validación cruzada, procedemos a identificar los mejores parámetros para crear nuestro modelo Random Forest
grid_search.best_params_
{'bootstrap': True,
'max_depth': 100,
'max_features': 6,
'min_samples_leaf': 3,
'min_samples_split': 8,
'n_estimators': 100}
ranf = RandomForestClassifier(bootstrap=True,
max_depth=100,
max_features=6,
min_samples_leaf=3,
min_samples_split=8,
n_estimators=100)
ranf.fit(X_train, y_train.ravel())
rfc_pred = ranf.predict(X_test)
Con los parámetros establecidos, creamos una gráfica que nos permita identificar la importancia de cada variable para clasificar los registros
importances = ranf.feature_importances_
indices = np.argsort(importances)[::-1]
plt.figure()
plt.title("Importancia de las características")
plt.bar(range(X_train.shape[1]), importances[indices],
color="r", align="center")
xticks_labels = [dfModelo.columns[1+i] for i in indices]
plt.xticks(range(varIndep.shape[1]), xticks_labels, rotation=90)
plt.xlim([-1, varIndep.shape[1]])
plt.show()
Ahora creamos la matriz de confusión para ver de manera gráfica cómo predijo el modelo y cuáles eran los valores reales.
%config InlineBackend.figure_format = 'svg'
fig = plt.figure(figsize=(15,15))
ax1 = fig.add_subplot(1,3,1)
plot_confusion_matrix(ranf, X_test, y_test, cmap = 'Reds', ax=ax1).im_.colorbar.remove()
plt.title("Random Forest")
Text(0.5, 1.0, 'Random Forest')